package ddz.core.ai;

import com.kaka.util.IntArray;
import com.kaka.util.IntSet;
import com.kaka.util.MathUtils;
import ddz.core.CardType;
import ddz.core.Cards;
import ddz.core.Pile;
import ddz.core.Weight;

import java.util.*;

/**
 * 普通玩法AI
 *
 * @author zkpursuit
 */
public class AI {

    private static boolean isDan(Cards mycards) {
        return mycards.getCardCount() == 1;
    }

    private static boolean isDui(Cards mycards) {
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() > 0) {
            return false;
        }
        return mycards.getPileCount() == 1 && mycards.getCardCount() == 2;
    }

    private static boolean isSan(Cards mycards) {
        return mycards.getPileCount() == 1 && mycards.getCardCount() == 3;
    }

    public static boolean isWangZha(Cards mycards) {
        Pile wang = mycards.getJokerPile();
        return wang != null && wang.getCount() == 2 && mycards.getCardCount() == wang.getCount();
    }

    private static boolean isZha(Cards mycards) {
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() == 2 && mycards.getCardCount() == wang.getCount()) {
            return true;
        }
        return (wang == null || wang.getCount() == 0) && mycards.getPileCount() == 1 && mycards.getCardCount() == 4;
    }

    /**
     * 三带单张或者对
     *
     * @param mycards
     * @param daiNum  1为带单张，2为带对
     * @return
     */
    private static boolean _isSandai(Cards mycards, int daiNum) {
        int sanCount = 0, daiCount = 0;
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile cluster = mycards.getPileByIndex(i);
            if (cluster != null) {
                if (cluster.getCount() == 3) {
                    sanCount++;
                } else {
                    if (daiNum == 1) {
                        if (cluster.getCount() == daiNum) {
                            daiCount++;
                        }
                    } else {
                        if (cluster.getCount() == daiNum && cluster.getPoint() != 0) {
                            daiCount++;
                        }
                    }
                }
            }
        }
        return sanCount == 1 && daiCount == 1;
    }

    static boolean isSandaidan(Cards mycards) {
        if (mycards.getPileCount() != 2) {
            return false;
        }
        if (mycards.getCardCount() != 4) {
            return false;
        }
        return _isSandai(mycards, 1);
    }

    static boolean isSandaidui(Cards mycards) {
        if (mycards.getPileCount() != 2) {
            return false;
        }
        if (mycards.getCardCount() != 5) {
            return false;
        }
        return _isSandai(mycards, 2);
    }

    /**
     * 计算顺牌数量
     *
     * @param mycards 手牌
     * @param num     顺子中每一簇牌的张数，单顺为1，对顺为2，三顺为3
     * @param overNum 超出num是否计入顺牌个数
     * @return true为顺牌
     */
    static int countShun(Cards mycards, int num, boolean overNum) {
        int _shunNum = 0;
        for (int i = mycards.getMinValidIndex(); i < mycards.getMaxValidIndex(); i++) {
            Pile cluster1 = mycards.getPileByIndex(i);
            Pile cluster2 = mycards.getPileByIndex(i + 1);
            if (cluster1 != null && cluster2 != null) {
                if (cluster1.isSequenceOf(cluster2)) {
                    if ((overNum && cluster1.getCount() >= num && cluster2.getCount() >= num) ||
                            (!overNum && cluster1.getCount() == num && cluster2.getCount() == num))
                        _shunNum++;
                }
            }
        }
        return _shunNum + 1;
    }

    /**
     * 是否为连顺，可判断单顺、对顺、三顺
     *
     * @param mycards 手牌
     * @param shunNum 顺子个数
     * @param num     顺子中每一簇牌的张数，单顺为1，对顺为2，三顺为3
     * @return true为顺牌
     */
    static boolean isShun(Cards mycards, int shunNum, int num) {
        if (mycards.getPileCount() != shunNum) {
            return false;
        }
        if (mycards.getCardCount() != shunNum * num) {
            return false;
        }
        Pile _2 = mycards.getPileByIndex(Cards.getIndex(2));
        if (_2 != null && _2.getCount() > 0) {
            return false;
        }
        Pile _wang = mycards.getJokerPile();
        if (_wang != null && _wang.getCount() > 0) {
            return false;
        }
        int count = countShun(mycards, num, false);
        return count == shunNum && count * num == mycards.getCardCount();
    }

    static boolean isShunzi(Cards mycards) {
        int shunNum = mycards.getPileCount();
        if (shunNum < 5) {
            return false;
        }
        return isShun(mycards, shunNum, 1);
    }

    static boolean isLiandui(Cards mycards) {
        int shunNum = mycards.getPileCount();
        if (shunNum < 3) {
            return false;
        }
        return isShun(mycards, shunNum, 2);
    }

    public static boolean isFeiji(Cards mycards) {
        int shunNum = mycards.getPileCount();
        if (shunNum < 2) {
            return false;
        }
        return isShun(mycards, shunNum, 3);
    }

    /**
     * 是否为航天飞机
     *
     * @param mycards
     * @return
     */
    static boolean isLianzha(Cards mycards) {
        if (mycards.getCardCount() % 4 != 0) {
            return false;
        }
        int shunNum = mycards.getPileCount();
        if (shunNum < 2) {
            return false;
        }
        return isShun(mycards, shunNum, 4);
    }

    /**
     * 四张带两张
     *
     * @param mycards
     * @return
     */
    private static boolean isSidaier1(Cards mycards) {
        if (mycards.getCardCount() != 6) {
            return false;
        }
        int siCount = 0;
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile cluster = mycards.getPileByIndex(i);
            if (cluster != null && cluster.getCount() == 4) {
                siCount++;
            }
        }
        return siCount == 1;
    }

    /**
     * 四张带两对
     *
     * @param mycards
     * @return
     */
    private static boolean isSidaier2(Cards mycards) {
        if (mycards.getCardCount() != 8) {
            return false;
        }
        int siCount = 0, duiCount = 0;
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile cluster = mycards.getPileByIndex(i);
            if (cluster != null) {
                if (cluster.getCount() % 2 != 0) {
                    return false;
                }
                if (cluster.getCount() == 4) {
                    siCount++;
                } else if (cluster.getCount() == 2) {
                    duiCount++;
                }
            }
        }
        if (siCount == 2 && duiCount == 0) {
            return true;
        }
        return siCount == 1 && duiCount == 2;
    }

    /**
     * 判断是否为飞机带翅膀，可判断普通飞机带一张牌的翅膀或者航天飞机带两张牌的翅膀
     *
     * @param mycards 手牌
     * @param type    3：普通飞机，4：航天飞机
     * @return
     */
    private static boolean _isFeiji1(Cards mycards, int type) {
        int fudaiCount = type / 2;
        if (mycards.getCardCount() % (type + fudaiCount) != 0) {
            return false;
        }
        int shunCount = countShun(mycards, type, true);
        if (shunCount < 2) return false;
        int max = shunCount - 2;
        for (int i = 0; i <= max; i++) {
            int shuncount = shunCount - i;
            int remaining = mycards.getCardCount() - shuncount * type;
            if (shuncount >= 2 && shuncount * fudaiCount == remaining) return true;
        }
        return false;
    }

    /**
     * 飞机带翅膀，每三张带一张
     *
     * @param mycards
     * @return
     */
    public static boolean isFeiji1(Cards mycards) {
        return _isFeiji1(mycards, 3);
    }

    /**
     * 飞机带翅膀，每三张带一对
     *
     * @param mycards
     * @return
     */
    public static boolean isFeiji2(Cards mycards) {
        if (mycards.getCardCount() % 5 != 0) {
            return false;
        }
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() > 0) {
            return false;
        }
        int duiCount = 0;
        //只要存在单牌直接判否
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null) {
                if (pile.getCount() == 1) {
                    return false;
                }
                if (pile.getCount() == 2 || pile.getCount() == 4) {
                    duiCount += pile.getCount() / 2;
                }
            }
        }
        int shunCount = countShun(mycards, 3, false);
        if (shunCount == duiCount) return true;
        return false;
    }

    public static CardType judgeType(Cards mycards, boolean lianzha) {
        if (isDan(mycards)) {
            return CardType.dan;
        }
        if (isDui(mycards)) {
            return CardType.dui;
        }
        if (isSan(mycards)) {
            return CardType.san;
        }
        if (isZha(mycards)) {
            return CardType.zha;
        }
        if (isSandaidan(mycards)) {
            return CardType.san_dan;
        }
        if (isSandaidui(mycards)) {
            return CardType.san_dui;
        }
        if (isShunzi(mycards)) {
            return CardType.dan_shun;
        }
        if (isLiandui(mycards)) {
            return CardType.dui_shun;
        }
        if (lianzha && isLianzha(mycards)) {
            return CardType.lianzha;
        }
        if (isSidaier1(mycards)) {
            return CardType.si_dan;
        }
        if (isSidaier2(mycards)) {
            return CardType.si_dui;
        }
        if (isFeiji(mycards)) {
            return CardType.san_shun;
        }
        if (isFeiji1(mycards)) {
            return CardType.feiji_dan;
        }
        if (isFeiji2(mycards)) {
            return CardType.feiji_dui;
        }
        return CardType.none;
    }

    /**
     * 获取三带、飞机翅膀、航天飞机翅膀牌的主体牌组
     *
     * @param mycards
     * @param pileCardCount
     * @return
     */
    private static Pile getSubjectPile(Cards mycards, int pileCardCount) {
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null) {
                if (pile.getCount() == pileCardCount) {
                    return pile;
                }
            }
        }
        return null;
    }

    /**
     * 比较牌类型大小时获取最小的牌簇
     *
     * @param mycards 比较的牌组集合
     * @param type    牌类型
     * @return 最小牌组
     */
    public static Pile getMinPile(Cards mycards, CardType type) {
        switch (type) {
            case dan:
            case dui:
            case san:
            case zha:
            case dan_shun:
            case dui_shun:
            case san_shun:
                return mycards.getPileByIndex(mycards.getMinValidIndex());
            case san_dan:
            case san_dui:
            case feiji_dan:
            case feiji_dui:
                return getSubjectPile(mycards, 3);
            case si_dan:
            case si_dui:
            case lianzha:
                return getSubjectPile(mycards, 4);
        }
        return null;
    }

    private static int getStartIndex(Cards mycards, int minCard) {
        int start = mycards.getMinValidIndex();
        if (minCard > 0) {
            int idx = Cards.getIndex(minCard);
            idx++;
            if (idx > mycards.getMaxValidIndex()) {
                start = mycards.getMaxValidIndex();
            } else {
                start = idx;
            }
        }
        return start;
    }

    public static List<int[]> dan(Cards mycards, int minCard) {
        int start = getStartIndex(mycards, minCard);
        List<int[]> list = new ArrayList<>(mycards.getPileCount());
        for (int i = start; i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.compare(minCard) > 0) {
                if (pile.getCount() == 1 || pile.getCount() == 3
                        || (pile.getCount() == 2 && pile.getPoint() != 0)) {
                    int[] cards = pile.getCards();
                    list.add(new int[]{cards[0]});
                }
            }
        }
        return list;
    }

    public static List<int[]> dui(Cards mycards, int minCard) {
        int start = getStartIndex(mycards, minCard);
        List<int[]> list = new ArrayList<>(mycards.getPileCount());
        for (int i = start; i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.compare(minCard) > 0) {
                if (pile.getCount() == 3
                        || (pile.getCount() == 2 && pile.getPoint() != 0)) {
                    int[] cards = pile.getCards();
                    list.add(new int[]{cards[0], cards[1]});
                }
            }
        }
        return list;
    }

    public static List<int[]> san(Cards mycards, int minCard) {
        int start = getStartIndex(mycards, minCard);
        List<int[]> list = new ArrayList<>(mycards.getPileCount());
        for (int i = start; i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.compare(minCard) > 0) {
                if (pile.getCount() >= 3) {
                    int[] cards = pile.getCards();
                    int[] newCards = new int[3];
                    System.arraycopy(cards, 0, newCards, 0, newCards.length);
                    list.add(newCards);
                }
            }
        }
        return list;
    }

    public static List<int[]> zha(Cards mycards, int minCard) {
        int start = getStartIndex(mycards, minCard);
        List<int[]> list = new ArrayList<>(mycards.getPileCount());
        int[] wangZha = null;
        for (int i = start; i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.compare(minCard) > 0) {
                if (pile.getCount() == 4) {
                    int[] cards = pile.getCards();
                    list.add(cards);
                } else if (pile.getCount() == 2 && pile.getPoint() == 0) {
                    wangZha = pile.getCards();
                }
            }
        }
        if (wangZha != null) list.add(wangZha);
        return list;
    }

    private static List<CardWrap> danOfDai(Cards mycards) {
        List<CardWrap> danCards = new ArrayList<>(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() > 0) {
                int[] pileCards = pile.getCards();
                //单牌基础权重为100，癞子牌为700，鬼牌为300，对为400，三为500，四为800，王炸为1000
                for (int card : pileCards) {
                    CardWrap wrap = new CardWrap(new int[]{card});
                    if (pile.getCount() == 1) {
                        if (pile.getPoint() == 0) {
                            wrap.weight = 300;
                        } else {
                            wrap.weight = 100;
                        }
                    } else if (pile.getCount() == 2) {
                        if (pile.getPoint() == 0) {
                            wrap.weight = 1000;
                        } else {
                            wrap.weight = 400;
                        }
                    } else if (pile.getCount() == 3) {
                        wrap.weight = 500;
                    } else if (pile.getCount() == 4) {
                        wrap.weight = 800;
                    }
                    //单牌的基础权重还得加上牌点，根据牌点也是从小到大
                    int p = pile.getPoint();
                    if (p == 2) p = 20;
                    wrap.weight += p;
                    danCards.add(wrap);
                }
            }
        }
        //根据单张的权重排序，
        CardWrap.sortCardWrapList(danCards);
        return danCards;
    }

    private static List<CardWrap> duiOfDai(Cards mycards, boolean zhaToTwoDui) {
        int max = mycards.getMaxValidIndex();
        int _2_idx = Cards.getIndex(2);
        if (max > _2_idx) max = _2_idx;
        List<CardWrap> duiList = new ArrayList<>(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= max; i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() >= 2) {
                //基础权重：纯癞子牌为700，本身对为100，三为500，四为800，有癞子填充的对为300
                if (pile.getCount() == 4 && zhaToTwoDui) {
                    int[] pileCards = pile.getCards();
                    CardWrap wrap1 = new CardWrap(new int[]{pileCards[0], pileCards[1]});
                    wrap1.weight = 800;
                    duiList.add(wrap1);
                    CardWrap wrap2 = new CardWrap(new int[]{pileCards[2], pileCards[3]});
                    wrap2.weight = 800;
                    duiList.add(wrap2);
                } else {
                    int[] pileCards = pile.getCards();
                    CardWrap wrap = new CardWrap(pileCards);
                    if (pile.getCount() == 2 && pile.getPoint() != 0) {
                        wrap.weight = 100;
                    } else if (pile.getCount() == 3) {
                        wrap.weight = 500;
                    } else if (pile.getCount() == 4 && !zhaToTwoDui) {
                        wrap.weight = 800;
                    }
                    duiList.add(wrap);
                }
            }
        }
        //根据单张的权重排序，
        CardWrap.sortCardWrapList(duiList);
        return duiList;
    }

    private static void remove(Cards mycards, int[] cards, boolean removePile, IntSet tempRemovedCards) {
        for (int card : cards) {
            if (removePile) {
                int pileIndex = Cards.getIndex(card);
                Pile pile = mycards.getPileByIndex(pileIndex);
                if (pile != null && pile.getCount() > 0) {
                    int[] _cards = pile.getCards();
                    for (int cc : _cards) {
                        mycards.removeCard(cc);
                        tempRemovedCards.add(cc);
                    }
                }
            }
            if (mycards.removeCard(card)) {
                tempRemovedCards.add(card);
            }
        }
    }

    private static void restore(Cards mycards, IntSet tempRemovedCards) {
        IntSet.IntSetIterator iterator = tempRemovedCards.iterator();
        while (iterator.hasNext) {
            int card = iterator.next();
            mycards.addCard(card);
        }
    }

    private static List<int[]> san_si_1(Cards mycards, int type, int minCard) {
        int daiAmount = type / 2; //附带单牌数量
        int minAmount = type + daiAmount;
        List<int[]> mainList;
        if (type == 3) {
            mainList = san(mycards, minCard);
        } else {
            mainList = zha(mycards, minCard);
        }
        List<int[]> groups = new ArrayList<>();
        IntSet tempRemovedCards = new IntSet(type);
        for (int[] cards : mainList) {
            if (cards.length < type) continue; //因为炸里面包含王炸
            //从手牌中移除主牌
            remove(mycards, cards, true, tempRemovedCards);
            //移除三张或四张主牌后从余下的牌中寻找单张，无单张拆2，无2拆3，无3拆炸
            List<CardWrap> danCards = danOfDai(mycards);
            if (danCards.size() >= daiAmount) {
                int[] group = new int[minAmount];
                System.arraycopy(cards, 0, group, 0, cards.length);
                int arrIndex = cards.length;
                for (int i = 0; i < daiAmount; i++, arrIndex++) {
                    CardWrap wrap = danCards.get(i);
                    int card = wrap.cards[0];
                    group[arrIndex] = card;
                }
                groups.add(group); //新增一组三带一张或者四带两张
            }
            //还原手牌
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    private static List<int[]> san_si_2(Cards mycards, int type, int minCard) {
        int minAmount = type + ((type / 2) * 2);
        int daiAmount = type / 2; //附带的对牌数量
        List<int[]> mainList;
        if (type == 3) {
            mainList = san(mycards, minCard);
        } else {
            mainList = zha(mycards, minCard);
        }
        List<int[]> groups = new ArrayList<>();
        IntSet tempRemovedCards = new IntSet(type);
        for (int[] cards : mainList) {
            if (cards.length < type) continue; //因为炸里面包含王炸
            //从手牌中移除主牌
            remove(mycards, cards, true, tempRemovedCards);
            //移除三张或四张主牌后从余下的牌中寻找单张，无单张拆2，无2拆3，无3拆炸
            List<CardWrap> duiCards = duiOfDai(mycards, true);
            if (duiCards.size() >= daiAmount) {
                int[] group = new int[minAmount];
                System.arraycopy(cards, 0, group, 0, cards.length);
                int arrIndex = cards.length;
                for (int i = 0; i < daiAmount; i++) {
                    CardWrap wrap = duiCards.get(i);
                    int[] wrapCards = wrap.cards;
                    System.arraycopy(wrapCards, 0, group, arrIndex, 2);
                    arrIndex += 2;
                }
                groups.add(group); //新增一组三带一张或者四带两张
            }
            //还原手牌
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    public static List<int[]> sandai1(Cards mycards, int minCard) {
        return san_si_1(mycards, 3, minCard);
    }

    public static List<int[]> sandai2(Cards mycards, int minCard) {
        return san_si_2(mycards, 3, minCard);
    }

    public static List<int[]> sidai1(Cards mycards, int minCard) {
        return san_si_1(mycards, 4, minCard);
    }

    public static List<int[]> sidai2(Cards mycards, int minCard) {
        return san_si_2(mycards, 4, minCard);
    }

    private static List<int[]> shun(Cards mycards, int minCard, int type, int minLianCount) {
        List<int[]> groups = new ArrayList<>();
        int lianCount = -1; //连牌数量
        int startIndex = -1, endIndex = -1; //连牌的开始索引和结束索引
        Pile tempPile = null; //连续的上一牌簇
        boolean isDiscon = false; //连续的牌是否断裂
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() >= type && pile.compare(minCard) > 0) {
                if (tempPile == null) {
                    lianCount = 1;
                    startIndex = i;
                    endIndex = i;
                    isDiscon = false;
                    tempPile = pile;
                } else {
                    if (tempPile.isSequenceOf(pile)) {
                        lianCount++;
                        endIndex = i;
                        if (i == mycards.getMaxValidIndex()) {
                            isDiscon = true;
                        }
                    } else {
                        isDiscon = true;
                    }
                    tempPile = pile;
                }
            } else {
                tempPile = null;
                isDiscon = true;
            }
            if (isDiscon && lianCount >= minLianCount) {
                int[] lianCards = new int[lianCount * type];
                for (int j = endIndex, idx = 0; j >= startIndex; j--, idx += type) {
                    int[] cards = mycards.getPileByIndex(j).getCards();
                    if (type == 1) lianCards[idx] = cards[0];
                    else System.arraycopy(cards, 0, lianCards, idx, type);
                }
                groups.add(lianCards);
                isDiscon = false;
                tempPile = null;
                lianCount = 1;
            }
        }
        return groups;
    }

    private static List<int[]> shun(Cards mycards, int minCard, int type, int minLianCount, boolean surplus_minCount) {
        List<int[]> maxShunGroups = shun(mycards, minCard, type, minLianCount);
        List<int[]> groups = new ArrayList<>();
        if (maxShunGroups.isEmpty()) {
            return groups;
        }
        maxShunGroups.forEach((int[] shun) -> {
            int[] delineateSrc = new int[shun.length / type];
            for (int i = 0; i < shun.length; i += type) {
                delineateSrc[i / type] = i;
            }
            MathUtils.delineate(delineateSrc, minLianCount, surplus_minCount, (int[] arr) -> {
                int[] group = new int[arr.length * type];
                int idx = 0;
                for (int i = 0; i < arr.length; i++) {
                    int max = arr[i] + type;
                    for (int j = arr[i]; j < max; j++) {
                        group[idx] = shun[j];
                        idx++;
                    }
                }
                groups.add(group);
            });
        });
        return groups;
    }

    public static List<int[]> danShun(Cards mycards, int minCard, int minLianCount) {
        return shun(mycards, minCard, 1, minLianCount, false);
    }

    public static List<int[]> duiShun(Cards mycards, int minCard, int minLianCount) {
        return shun(mycards, minCard, 2, minLianCount, false);
    }

    public static List<int[]> sanShun(Cards mycards, int minCard, int minLianCount) {
        return shun(mycards, minCard, 3, minLianCount, false);
    }

    public static List<int[]> lianZha(Cards mycards, int minCard, final int minLianCount) {
        int lianCount = minLianCount;
        if (lianCount <= 1) lianCount = 2;
        List<int[]> groups = shun(mycards, -1, 4, lianCount, true);
        if (!groups.isEmpty()) {
            if (minLianCount >= 2) {
                int i = 0, max = groups.size();
                while (i < max) {
                    int[] group = groups.get(i);
                    int zhaCount = group.length / 4;
                    if (zhaCount < minLianCount) {
                        groups.remove(i);
                        max--;
                        continue;
                    }
                    if (zhaCount == minLianCount) {
                        if (Cards.compare(group[0], minCard) <= 0) {
                            groups.remove(i);
                            max--;
                            continue;
                        }
                    }
                    i++;
                }
            }
        }
        if (!groups.isEmpty()) {
            sortByWeight(groups, mycards);
            Collections.reverse(groups);
        }
        return groups;
    }

    private static List<int[]> feiji(Cards mycards, int minCard, int shunNum, int daiType) {
        List<int[]> sanShuns = sanShun(mycards, minCard, shunNum);
        List<int[]> groups = new ArrayList<>();
        if (sanShuns.isEmpty()) return groups;
        IntSet tempRemovedCards = new IntSet(16);
        for (int[] sanShun : sanShuns) {
            remove(mycards, sanShun, false, tempRemovedCards);
            List<CardWrap> daiWraps;
            if (daiType == 1) {
                daiWraps = danOfDai(mycards);
            } else {
                daiWraps = duiOfDai(mycards, true);
            }
            int lianCount = sanShun.length / 3;
            if (daiWraps.size() >= lianCount) {
                if (daiType == 1) {
                    int[] group = new int[sanShun.length + lianCount];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    int idx = sanShun.length;
                    for (int i = 0; i < lianCount; i++) {
                        CardWrap wrap = daiWraps.get(i);
                        group[idx] = wrap.cards[0];
                        idx++;
                    }
                    groups.add(group);
                } else {
                    int[] group = new int[sanShun.length + lianCount * 2];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    int idx = sanShun.length;
                    for (int i = 0; i < lianCount; i++) {
                        CardWrap wrap = daiWraps.get(i);
                        System.arraycopy(wrap.cards, 0, group, idx, 2);
                        idx += 2;
                    }
                    groups.add(group);
                }
            }
            restore(mycards, tempRemovedCards);
        }
        return groups;
    }

    public static List<int[]> feiji1(Cards mycards, int minCard, int minLianCount) {
        return feiji(mycards, minCard, minLianCount, 1);
    }

    public static List<int[]> feiji2(Cards mycards, int minCard, int minLianCount) {
        return feiji(mycards, minCard, minLianCount, 2);
    }

    private static Weight calcWeight(int[] group, Cards mycards, Weight weight, IntSet pointSet) {
        weight.reset();
        for (int card : group) {
            int cardPoint = Cards.getPoint(card);
            if (!pointSet.contains(cardPoint)) {
                Pile oPile = mycards.getPileByIndex(Cards.getIndex(cardPoint));
                if (oPile.getCount() == 2) {
                    weight.dui++;
                } else if (oPile.getCount() == 3) {
                    weight.san++;
                } else if (oPile.getCount() == 4) {
                    weight.zha++;
                } else if (oPile.getCount() == 1) {
                    weight.dan++;
                }
            }
            weight.count++;
        }
        if (weight.count == mycards.getCardCount()) {
            weight.dui = 0;
            weight.san = 0;
            weight.zha = 0;
            weight.dan = 0;
        }
        return weight;
    }

    /**
     * 根据权重数据排序
     *
     * @param groups
     * @param mycards
     * @return
     */
    private static List<int[]> sortByWeight(List<int[]> groups, Cards mycards) {
        if (groups.size() <= 1) return groups;
        final Weight weight1 = new Weight();
        final Weight weight2 = new Weight();
        final IntSet pointSet1 = new IntSet();
        final IntSet pointSet2 = new IntSet();
        groups.sort((int[] group1, int[] group2) -> {
            weight1.reset();
            weight2.reset();
            calcWeight(group1, mycards, weight1, pointSet1);
            calcWeight(group2, mycards, weight2, pointSet2);
            //炸弹越少优先级越高
            if (weight1.zha < weight2.zha) {
                return -1;
            }
            if (weight1.zha > weight2.zha) {
                return 1;
            }
            //拆掉的三张越少优先级越高
            if (weight1.san < weight2.san) {
                return -1;
            }
            if (weight1.san > weight2.san) {
                return 1;
            }
            //拆掉的对牌越少优先级越高
            if (weight1.dui < weight2.dui) {
                return -1;
            }
            if (weight1.dui > weight2.dui) {
                return 1;
            }
            //单牌越多优先级越高
            if (weight1.dan < weight2.dan) {
                return 1;
            }
            if (weight1.dan > weight2.dan) {
                return -1;
            }
            //牌张数越多优先级越高
            if (weight1.count < weight2.count) {
                return 1;
            }
            if (weight1.count > weight2.count) {
                return -1;
            }
            return 0;
        });
        return groups;
    }

    /**
     * 插入炸弹
     *
     * @param groups  所有的牌簇集合组
     * @param mycards 手牌
     * @return 插入炸弹后的牌簇集合组
     */
    public static List<int[]> insertBomb(List<int[]> groups, Cards mycards, boolean lianZha) {
        IntSet inserted = new IntSet();
        List<int[]> list = new ArrayList<>();
        for (int[] group : groups) {
            for (int card : group) {
                int cardPoint = Cards.getPoint(card);
                Pile oPile = mycards.getPileByIndex(Cards.getIndex(cardPoint));
                if (oPile.getCount() == 4 && !inserted.contains(oPile.getPoint())) {
                    inserted.add(oPile.getPoint());
                    list.add(oPile.getCards());
                }
            }
            list.add(group);
        }
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() == 4 && !inserted.contains(pile.getPoint())) {
                list.add(pile.getCards());
            }
        }
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() == 2) {
            list.add(wang.getCards());
        }
        if (lianZha) {
            List<int[]> lzhas = lianZha(mycards, -1, 2);
            if (!lzhas.isEmpty()) list.addAll(lzhas);
        }
        return list;
    }

    /**
     * 调整牌簇集合组
     *
     * @param groups  牌簇集合组
     * @param mycards 手牌
     * @return 调整后的牌簇集合组
     */
    public static List<int[]> adjust(List<int[]> groups, Cards mycards, boolean lianZha) {
        List<int[]> list = sortByWeight(groups, mycards);
        list = insertBomb(list, mycards, lianZha);
        return list;
    }

    public static boolean isOver(Cards mycards, Cards prevChuCards, CardType prevCardType, boolean lianZha) {
        CardType type = AI.judgeType(mycards, lianZha);
        if (type == CardType.none) return false;
        boolean over = false;
        if (prevCardType != CardType.lianzha && type == CardType.lianzha) {
            //上家不是连炸，但本家出的连炸
            over = true;
        } else if (prevCardType != CardType.zha && prevCardType != CardType.lianzha && type == CardType.zha) {
            //上家打出非炸弹非连炸，本家用炸弹压
            over = true;
        } else if (prevCardType == type) {
            if (type == CardType.zha && mycards.getCardCount() == 2) { //都是炸弹
                //上家出炸弹，本家出王炸
                over = true;
            } else if (type == CardType.lianzha && mycards.getCardCount() > prevChuCards.getCardCount()) { //都是连炸
                //本家出的连炸数量比上家打出的多
                over = true;
            } else {
                //余下的炸弹或连炸或其它牌型必须保证数量一致
                if (prevChuCards.getCardCount() != mycards.getCardCount()) {
                    //牌张数与上家打出的牌张数不同
                    over = false;
                } else {
                    Pile prevMin = AI.getMinPile(prevChuCards, type);
                    Pile selfMin = AI.getMinPile(mycards, type);
                    if (selfMin.compare(prevMin) > 0) {
                        over = true;
                    }
                }
            }
        }
        return over;
    }

    public static List<int[]> beiDong(Cards mycards, Cards prevChuCards, CardType cardType, boolean lianZha) {
        Pile min = AI.getMinPile(prevChuCards, cardType);
        int minCard = min.getOneCard();
        List<int[]> groups = null;
        switch (cardType) {
            case dan:
                groups = AI.dan(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case dui:
                groups = AI.dui(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case san:
                groups = AI.san(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case san_dan:
                groups = AI.sandai1(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case san_dui:
                groups = AI.sandai2(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case dan_shun:
                groups = AI.danShun(mycards, minCard, prevChuCards.getPileCount());
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case dui_shun:
                groups = AI.duiShun(mycards, minCard, prevChuCards.getPileCount());
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case san_shun:
                groups = AI.sanShun(mycards, minCard, prevChuCards.getPileCount());
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case feiji_dan:
                groups = AI.feiji1(mycards, minCard, prevChuCards.getCardCount() / 4);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case feiji_dui:
                groups = AI.feiji2(mycards, minCard, prevChuCards.getCardCount() / 5);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case si_dan:
                groups = AI.sidai1(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case si_dui:
                groups = AI.sidai2(mycards, minCard);
                groups = AI.adjust(groups, mycards, lianZha);
                break;
            case zha:
                groups = AI.zha(mycards, minCard);
                if (lianZha) {
                    List<int[]> lzhas = lianZha(mycards, -1, 1);
                    if (!lzhas.isEmpty()) {
                        groups.addAll(lzhas);
                    }
                }
                break;
            case lianzha:
                if (lianZha) {
                    groups = AI.lianZha(mycards, minCard, prevChuCards.getPileCount());
                }
                break;
        }
        return groups;
    }

//    public static void main(String[] args) {
//        HandCards handCards = new HandCards();
//        //410, 310, 210, 110, 213, 311, 107, 206
//        int[] cards = new int[]{
//                402, 414, 114, 213, 113, 410, 310, 110, 409, 109, 208, 108, 407, 207, 107, 405, 205, 404, 204, 303
//        };
//        handCards.addCard(cards);
//        System.out.println(Arrays.toString(handCards.getCards()));
//        System.out.println("---------------------------------------------");
//
//        HandCards prevHandCards = new HandCards();
//        int[] prevCards = new int[]{
//                410, 310, 210, 110
//        };
//        prevHandCards.addCard(prevCards);
//        //List<int[]> groups = AI0.beiDong(handCards, prevHandCards, CardType.zha);
//        List<int[]> groups = AI0.zhuDong(handCards);
//        System.out.println(Arrays.deepToString(groups.toArray()));
//    }

    //////////////////////////////////////////////////////////////////////////////////////////

    public static List<int[]> zhuDong(Cards mycards) {
        Cards handCards = mycards.clone();
        Pile wang = handCards.getJokerPile();
        Map<Integer, Pile> zhaMap = new LinkedHashMap<>(4);
        Map<Integer, Pile> sanMap = new LinkedHashMap<>(6);
        for (int i = handCards.getMinValidIndex(); i <= handCards.getMaxValidIndex(); i++) {
            Pile pile = handCards.getPileByIndex(i);
            if (pile != null) {
                if (pile.getCount() == 4) {
                    zhaMap.put(pile.getPoint(), pile);
                    handCards.removePile(i);
                } else if (pile.getCount() == 3) {
                    sanMap.put(pile.getPoint(), pile);
                } else if (pile.getCount() == 2 && pile.getPoint() == 0) {
                    handCards.removePile(i);
                }
            }
        }
        List<IntArray> sanshun = AIBot.shun(handCards, -1, 3, 2, null);
        if (!sanshun.isEmpty()) {
            for (IntArray shun : sanshun) {
                for (int i = 0; i < shun.size(); i += 3) {
                    int point = Cards.getPoint(shun.get(i));
                    int pileIndex = Cards.getIndex(point);
                    handCards.removePile(pileIndex);
                    sanMap.remove(point);
                }
            }
        }
        sanMap.forEach((Integer point, Pile pile) -> {
            handCards.addCard(pile.getCards());
        });
        List<IntArray> danshun = AIBot.shun(handCards, -1, 1, 5, null);
        if (!danshun.isEmpty()) {
            for (IntArray shun : danshun) {
                for (int i = 0; i < shun.size(); i++) {
                    int card = shun.get(i);
                    handCards.removeCard(card);
                    int point = Cards.getPoint(shun.get(i));
                    if (sanMap.containsKey(point)) sanMap.remove(point);
                }
            }
        }
        List<IntArray> duishun = AIBot.shun(handCards, -1, 2, 3, null);
        if (!duishun.isEmpty()) {
            for (IntArray shun : duishun) {
                for (int i = 0; i < shun.size(); i++) {
                    int card = shun.get(i);
                    handCards.removeCard(card);
                    int point = Cards.getPoint(card);
                    if (sanMap.containsKey(point)) sanMap.remove(point);
                }
            }
        }
        for (int i = handCards.getMinValidIndex(); i <= handCards.getMaxValidIndex(); i++) {
            Pile pile = handCards.getPileByIndex(i);
            if (pile != null) {
                if (pile.getCount() == 3) {
                    handCards.removePile(Cards.getIndex(pile.getPoint()));
                    sanMap.put(pile.getPoint(), pile);
                }
            }
        }
        List<int[]> dui = dui(handCards, -1);
        if (!dui.isEmpty()) {
            for (int[] d : dui) {
                for (int card : d) {
                    handCards.removeCard(card);
                }
            }
        }
        List<int[]> dan = dan(handCards, -1);
        List<int[]> groups = new ArrayList<>();
        if (!sanshun.isEmpty()) {
            for (IntArray shun : sanshun) {
                int[] shunCards = shun.toArray();
                int shunCount = shun.size() / 3;
                if (dan.size() >= shunCount) {
                    int[] group = new int[shun.size() + shunCount];
                    System.arraycopy(shunCards, 0, group, 0, shunCards.length);
                    int idx = shunCards.length;
                    for (int i = 0; i < shunCount; i++) {
                        int[] danCard = dan.remove(0);
                        group[idx] = danCard[0];
                        idx++;
                    }
                    groups.add(group);
                } else {
                    groups.add(shunCards);
                }
            }
        }
        sanMap.forEach((Integer point, Pile pile) -> {
            int[] sanCards = pile.getCards();
            if (dan.size() >= 1) {
                int[] group = new int[sanCards.length + 1];
                System.arraycopy(sanCards, 0, group, 0, sanCards.length);
                int[] danCard = dan.remove(0);
                group[sanCards.length] = danCard[0];
                groups.add(group);
            } else {
                groups.add(sanCards);
            }
        });
        danshun.forEach((IntArray arr) -> {
            groups.add(arr.toArray());
        });
        duishun.forEach((IntArray arr) -> {
            groups.add(arr.toArray());
        });
        groups.addAll(dui);
        groups.addAll(dan);
        zhaMap.forEach((Integer point, Pile pile) -> {
            groups.add(pile.getCards());
        });
        if (wang != null && wang.getCount() == 2) {
            groups.add(wang.getCards());
        }
        return groups;
    }

}
